
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iostream>

#include "../utility/assert_helper.h"
#include "../utility/file_helper.h"
#include "reg_accessor.h"
#include "windows_information.h"
#include "windows_manager.h"
#include "sys_search.h"
#include "reg_accessor.h"

const char * MOUNTED_PARTITION_LOCATION = "/media";
const int	MAX_WINDOWS_NUMBER = 8;
const int	WINDOWS_SIGNS_TOTAL = 14;
const char	*NT_FAMILY_SIGN[WINDOWS_SIGNS_TOTAL] = {
	// critical folder
	"system/SHELL.DLL",
	"system32",
	"Temp",
	"Fonts",

	// system kernel file
	"system32/ntoskrnl.exe",
	"system32/hal.dll",
	"system32/win32k.sys",
	"system32/ntdll.dll",
	"system32/kernel32.dll",
	"system32/advapi32.dll",
	"system32/user32.dll",
	"system32/gdi32.dll",

	// register file
	"system32/config/system",
	// driver file
	"system32/drivers/vga.sys"
};

const char	* VERIFY_VERSION_FILE = "user32.dll";

Windows_Searcher::Windows_Searcher(Windows_Information_Collection & win_info_group) :
windows_info_group_(win_info_group)
{
}

Windows_Searcher::~Windows_Searcher()
{
}

int
Windows_Searcher::search_all_windows_in_mounted_partition()
{
	const char *non_windows_dir[] =
	{"swap", "zip", "ram1", "msdos", "floppy", "flash", "dvd", "data", "cdrom", "home", "..", "."};
	// travel /mnt expect non_windows_dir for find all folder whitch may be has windows
	return foreach_sub_directories_in_path(MOUNTED_PARTITION_LOCATION, deal_dir_whitch_may_be_was_windows_root,
	sizeof(non_windows_dir) / sizeof(char *), non_windows_dir, this);
}

std::vector<std::string>
Windows_Searcher::enum_all_sub_directories_in_path(const char * path)
{
	std::vector<std::string> ret;
	return ret;
}

int
Windows_Searcher::foreach_sub_directories_in_path(const char * path, travel_callback travel,
int except_num, const char ** except, void *userdata) const
{
 	IF_NULL_RETURN_VALUE(path, -1);
 	IF_NULL_RETURN_VALUE(travel, -1);
 	assert(except_num >= 0);
 	if (except_num < 0) return -1;

	DIR		*dir = NULL;
   struct	dirent *ent = NULL;
	char		directory[PATH_MAX + 1] = {0};
	bool		is_except = false;

   if (NULL == (dir = opendir(path)))
   {
     //perror("Unable to open directory");
     return -1;
   }
   while (NULL != (ent = readdir(dir)))
   {
		if (DT_DIR == ent->d_type)
		{
			if (NULL != except) {
				for (int i = 0; i < except_num; ++i) {
					if (0 == strcmp(ent->d_name, except[i])) {// 是要排除的路径
						is_except = true;
						break;
					}
				}
			}
			if (NULL != travel) {
				if (!is_except) {
					sprintf(directory, "%.*s/%.*s", PATH_MAX / 2, path, PATH_MAX / 2, ent->d_name);
					travel(directory, userdata);
				} else {
					is_except = false;
				}
			}
		}
   }

   if (0 != closedir(dir)) return -1;
	return 0;
}

void
Windows_Searcher::deal_dir_whitch_may_be_was_windows_root(const char *path, void *data)
{
 	IF_NULL_RETURN(path);
 	IF_NULL_RETURN(data);

	const char *except[2] = {".", ".."};
	// travel main_path except . & .. for find windows
	Windows_Searcher * win_searcher = reinterpret_cast<Windows_Searcher *>(data);
	if (NULL != win_searcher)
	win_searcher->foreach_sub_directories_in_path(path, try_to_find_windows_in_directory,
	sizeof(except)/ sizeof(char *), except, data);
}

void
Windows_Searcher::try_to_find_windows_in_directory(const char *path, void *data)
{
	IF_NULL_RETURN(path);
	IF_NULL_RETURN(data);
	int	integrity = 0;
	Windows_Searcher * win_searcher = reinterpret_cast<Windows_Searcher *>(data);
	Windows_Information	* win_info = NULL;
	char		intact_path[PATH_MAX + 1] = {0};

	for (int i = 0; i < WINDOWS_SIGNS_TOTAL; ++i)
	{
		sprintf(intact_path, "%.*s/%.*s", PATH_MAX / 2, path, PATH_MAX / 2, NT_FAMILY_SIGN[i]);
		if (utility::is_file_exist(intact_path)) {
			++integrity;
			printf("Find sign file %s.\n", intact_path);
		}
	}
	if (integrity > WINDOWS_SIGNS_TOTAL / 2) // check integrity of windows
	{
		win_info = win_searcher->analyze_win_root_for_windows_information(path);
		if (NULL != win_info)
		{
			printf("Find a windows\n");
			win_searcher->windows_info_group_.add_windows_information(win_info);
			return ;
		}
	}
}

Windows_Information *
Windows_Searcher::analyze_win_root_for_windows_information(const char * win_root) const
{
	IF_NULL_RETURN_VALUE(win_root, false);

	return new Windows_Information(win_root, get_windows_edition(win_root));
}

bool
Windows_Searcher::get_windows_version_and_build_number(const char * win_root,
int & major_ver, int & minor_ver, int & build_num)
{
	IF_NULL_RETURN_VALUE(win_root, false);
	int major = 0, minor = 0;

	std::string verify_file(std::string(win_root) + "/system32/" + VERIFY_VERSION_FILE);
	if (utility::get_pe_file_version(verify_file.c_str(), major, minor)) {
		major_ver = static_cast<unsigned short>(major >> 16);
		minor_ver = static_cast<unsigned short>(major & 0XFFFF);
		build_num = static_cast<unsigned short>(minor >> 16);
		return true;
	}
	return false;
}

Windows_Edition
Windows_Searcher::get_windows_edition_from_version_and_build_number(
int major_ver, int minor_ver, int build_num)
{
	switch (major_ver) {
		case 1:
		case 2:
		case 3:
		{
			if (51 == minor_ver && 1057 == build_num) {
				return WINDOWS_NT;
			} else {
				return WINDOWS_CE;
			}
		}
		break;
		case 4:
		{
			if (minor_ver < 10 && build_num < 1998) {
				return WINDOWS_95;
			} else if (10 == minor_ver && build_num >=1998 && build_num < 3000) {
				return WINDOWS_98;
			} else if (90 == minor_ver && 3000 == build_num) {
				return WINDOWS_ME;
			} else if (0 == minor_ver && 1381 == build_num) {
				return WINDOWS_NT;
			}
		}
		break;
		case 5:
			if (0 == minor_ver && 2195 == build_num) {
				return WINDOWS_2000;
			} else if (1 == minor_ver && 2600 == build_num) {
				return WINDOWS_XP;
			} else if (2 == minor_ver && 3790 == build_num) {
				return WINDOWS_2003;
			}
		break;
		case 6:
			if (0 == minor_ver && (build_num >= 6000 && build_num <= 6001)) {
				return WINDOWS_VISTA;
			} else if (1 == minor_ver /*&& ???? == build_num*/) {
				return WINDOWS_2008;
			}
		break;
		default :
			return WINDOWS_UNKNOW;
		break;
	}
	return WINDOWS_UNKNOW;
}

Windows_Edition
Windows_Searcher::get_windows_edition(const char * win_root)
{
	IF_NULL_RETURN_VALUE(win_root, WINDOWS_UNKNOW);
	int major_ver = 0, minor_ver = 0, build_num = 0;
	if (get_windows_version_and_build_number(win_root, major_ver, minor_ver, build_num)) {
		return get_windows_edition_from_version_and_build_number(major_ver, minor_ver, build_num);
	}
	return WINDOWS_UNKNOW;
}
